package service

import (
	"context"
	"strings"

	"github.com/gogf/gf/v2/errors/gerror"
	"github.com/gogf/gf/v2/frame/g"
	"github.com/gogf/gf/v2/net/ghttp"
	"github.com/gogf/gf/v2/os/gtime"

	"goldtree/internal/dao"
	"goldtree/internal/entity"
	"goldtree/utility/crypto"
	"goldtree/utility/jwt"
	"goldtree/utility/validate"
)

// UserService 用户服务
type UserService struct{}

var User = UserService{}

// RegisterReq 用户注册请求
type RegisterReq struct {
	Username string `json:"username" v:"required|length:3,20#用户名不能为空|用户名长度为3-20位"`
	Password string `json:"password" v:"required|length:6,32#密码不能为空|密码长度为6-32位"`
	Email    string `json:"email" v:"email#邮箱格式不正确"`
	Phone    string `json:"phone"`
	Nickname string `json:"nickname" v:"length:1,50#昵称长度不能超过50位"`
}

// LoginReq 用户登录请求
type LoginReq struct {
	Username string `json:"username" v:"required#用户名不能为空"`
	Password string `json:"password" v:"required#密码不能为空"`
}

// LoginRes 登录响应
type LoginRes struct {
	User         *entity.User `json:"user"`
	AccessToken  string       `json:"access_token"`
	RefreshToken string       `json:"refresh_token"`
	ExpiresIn    int64        `json:"expires_in"`
}

// RefreshTokenReq 刷新令牌请求
type RefreshTokenReq struct {
	RefreshToken string `json:"refresh_token" v:"required#刷新令牌不能为空"`
}

// ChangePasswordReq 修改密码请求
type ChangePasswordReq struct {
	OldPassword string `json:"old_password" v:"required#原密码不能为空"`
	NewPassword string `json:"new_password" v:"required|length:6,32#新密码不能为空|新密码长度为6-32位"`
}

// UpdateProfileReq 更新个人信息请求
type UpdateProfileReq struct {
	Email    string `json:"email" v:"email#邮箱格式不正确"`
	Phone    string `json:"phone"`
	Nickname string `json:"nickname" v:"length:1,50#昵称长度不能超过50位"`
	Avatar   string `json:"avatar"`
}

// Register 用户注册
func (s *UserService) Register(ctx context.Context, req *RegisterReq) (*entity.User, error) {
	// 验证用户名格式
	if err := s.validateUsername(req.Username); err != nil {
		return nil, err
	}
	
	// 验证密码强度
	if err := crypto.ValidatePasswordStrength(req.Password); err != nil {
		return nil, err
	}
	
	// 验证邮箱格式
	if req.Email != "" {
		if err := validate.Email(req.Email); err != nil {
			return nil, err
		}
	}
	
	// 验证手机号格式
	if req.Phone != "" {
		if err := validate.Phone(req.Phone); err != nil {
			return nil, err
		}
	}
	
	// 检查用户名是否已存在
	exists, err := dao.User.IsUsernameExists(ctx, req.Username)
	if err != nil {
		return nil, gerror.Wrap(err, "检查用户名失败")
	}
	if exists {
		return nil, gerror.New("用户名已存在")
	}
	
	// 检查邮箱是否已存在
	if req.Email != "" {
		exists, err := dao.User.IsEmailExists(ctx, req.Email)
		if err != nil {
			return nil, gerror.Wrap(err, "检查邮箱失败")
		}
		if exists {
			return nil, gerror.New("邮箱已存在")
		}
	}
	
	// 加密密码
	hashedPassword, err := crypto.GeneratePasswordHash(req.Password)
	if err != nil {
		return nil, gerror.Wrap(err, "密码加密失败")
	}
	
	// 创建用户
	user := &entity.User{
		Username:  req.Username,
		Password:  hashedPassword,
		Email:     req.Email,
		Phone:     req.Phone,
		Nickname:  req.Nickname,
		Role:      "user", // 默认用户角色
		Status:    "active",
		CreatedAt: gtime.Now(),
		UpdatedAt: gtime.Now(),
	}
	
	userId, err := dao.User.Insert(ctx, user)
	if err != nil {
		return nil, gerror.Wrap(err, "创建用户失败")
	}
	
	// 获取创建的用户信息
	user, err = dao.User.GetById(ctx, int(userId))
	if err != nil {
		return nil, gerror.Wrap(err, "获取用户信息失败")
	}
	
	g.Log().Info(ctx, "用户注册成功", "username", req.Username, "user_id", userId)
	
	return user, nil
}

// Login 用户登录
func (s *UserService) Login(ctx context.Context, req *LoginReq) (*LoginRes, error) {
	// 获取客户端IP
	request := g.RequestFromCtx(ctx)
	clientIP := s.getClientIP(request)
	
	// 根据用户名获取用户信息
	user, err := dao.User.GetByUsername(ctx, req.Username)
	if err != nil {
		return nil, gerror.Wrap(err, "获取用户信息失败")
	}
	if user == nil {
		return nil, gerror.New("用户名或密码错误")
	}
	
	// 检查用户状态
	if user.Status != "active" {
		return nil, gerror.New("用户账户已被锁定或停用")
	}
	
	// 验证密码
	if !crypto.VerifyPassword(req.Password, user.Password) {
		return nil, gerror.New("用户名或密码错误")
	}
	
	// 生成访问令牌
	accessToken, err := jwt.GenerateToken(user.Id, user.Username, user.Role)
	if err != nil {
		return nil, gerror.Wrap(err, "生成访问令牌失败")
	}
	
	// 生成刷新令牌
	refreshToken, err := jwt.GenerateRefreshToken(user.Id, user.Username)
	if err != nil {
		return nil, gerror.Wrap(err, "生成刷新令牌失败")
	}
	
	// 保存访问令牌
	accessTokenEntity := &entity.UserToken{
		UserId:    user.Id,
		Token:     accessToken,
		TokenType: "access",
		ExpiresAt: jwt.GetTokenExpireTime(),
		CreatedAt: gtime.Now(),
	}
	_, err = dao.UserToken.Insert(ctx, accessTokenEntity)
	if err != nil {
		return nil, gerror.Wrap(err, "保存访问令牌失败")
	}
	
	// 保存刷新令牌
	refreshTokenEntity := &entity.UserToken{
		UserId:    user.Id,
		Token:     refreshToken,
		TokenType: "refresh",
		ExpiresAt: jwt.GetRefreshTokenExpireTime(),
		CreatedAt: gtime.Now(),
	}
	_, err = dao.UserToken.Insert(ctx, refreshTokenEntity)
	if err != nil {
		return nil, gerror.Wrap(err, "保存刷新令牌失败")
	}
	
	// 更新登录信息
	err = dao.User.UpdateLoginInfo(ctx, user.Id, clientIP)
	if err != nil {
		g.Log().Warning(ctx, "更新登录信息失败", "error", err)
	}
	
	g.Log().Info(ctx, "用户登录成功", "username", req.Username, "user_id", user.Id, "ip", clientIP)
	
	return &LoginRes{
		User:         user,
		AccessToken:  accessToken,
		RefreshToken: refreshToken,
		ExpiresIn:    jwt.GetTokenExpireTime().Unix(),
	}, nil
}

// RefreshToken 刷新访问令牌
func (s *UserService) RefreshToken(ctx context.Context, req *RefreshTokenReq) (*LoginRes, error) {
	// 验证刷新令牌格式
	if err := jwt.ValidateTokenFormat(req.RefreshToken); err != nil {
		return nil, err
	}
	
	// 获取刷新令牌信息
	tokenInfo, err := dao.UserToken.GetByToken(ctx, req.RefreshToken)
	if err != nil {
		return nil, gerror.Wrap(err, "获取令牌信息失败")
	}
	if tokenInfo == nil {
		return nil, gerror.New("刷新令牌无效")
	}
	
	// 检查令牌类型
	if tokenInfo.TokenType != "refresh" {
		return nil, gerror.New("令牌类型错误")
	}
	
	// 检查令牌是否过期
	if tokenInfo.ExpiresAt.Before(gtime.Now()) {
		return nil, gerror.New("刷新令牌已过期")
	}
	
	// 获取用户信息
	user, err := dao.User.GetById(ctx, tokenInfo.UserId)
	if err != nil {
		return nil, gerror.Wrap(err, "获取用户信息失败")
	}
	if user == nil {
		return nil, gerror.New("用户不存在")
	}
	
	// 检查用户状态
	if user.Status != "active" {
		return nil, gerror.New("用户账户已被锁定或停用")
	}
	
	// 生成新的访问令牌
	accessToken, err := jwt.GenerateToken(user.Id, user.Username, user.Role)
	if err != nil {
		return nil, gerror.Wrap(err, "生成访问令牌失败")
	}
	
	// 保存新的访问令牌
	accessTokenEntity := &entity.UserToken{
		UserId:    user.Id,
		Token:     accessToken,
		TokenType: "access",
		ExpiresAt: jwt.GetTokenExpireTime(),
		CreatedAt: gtime.Now(),
	}
	_, err = dao.UserToken.Insert(ctx, accessTokenEntity)
	if err != nil {
		return nil, gerror.Wrap(err, "保存访问令牌失败")
	}
	
	g.Log().Info(ctx, "刷新令牌成功", "user_id", user.Id)
	
	return &LoginRes{
		User:         user,
		AccessToken:  accessToken,
		RefreshToken: req.RefreshToken, // 保持原刷新令牌
		ExpiresIn:    jwt.GetTokenExpireTime().Unix(),
	}, nil
}

// Logout 用户登出
func (s *UserService) Logout(ctx context.Context, token string) error {
	// 验证令牌格式
	if err := jwt.ValidateTokenFormat(token); err != nil {
		return err
	}
	
	// 撤销访问令牌
	err := dao.UserToken.RevokeToken(ctx, token)
	if err != nil {
		return gerror.Wrap(err, "撤销令牌失败")
	}
	
	g.Log().Info(ctx, "用户登出成功")
	
	return nil
}

// ChangePassword 修改密码
func (s *UserService) ChangePassword(ctx context.Context, userId int, req *ChangePasswordReq) error {
	// 获取用户信息
	user, err := dao.User.GetById(ctx, userId)
	if err != nil {
		return gerror.Wrap(err, "获取用户信息失败")
	}
	if user == nil {
		return gerror.New("用户不存在")
	}
	
	// 验证原密码
	if !crypto.VerifyPassword(req.OldPassword, user.Password) {
		return gerror.New("原密码错误")
	}
	
	// 验证新密码强度
	if err := crypto.ValidatePasswordStrength(req.NewPassword); err != nil {
		return err
	}
	
	// 加密新密码
	hashedPassword, err := crypto.GeneratePasswordHash(req.NewPassword)
	if err != nil {
		return gerror.Wrap(err, "密码加密失败")
	}
	
	// 更新密码
	updateUser := &entity.User{
		Password: hashedPassword,
	}
	err = dao.User.Update(ctx, userId, updateUser)
	if err != nil {
		return gerror.Wrap(err, "更新密码失败")
	}
	
	// 撤销所有访问令牌，强制重新登录
	err = dao.UserToken.RevokeUserTokens(ctx, userId, "access")
	if err != nil {
		g.Log().Warning(ctx, "撤销用户令牌失败", "error", err)
	}
	
	g.Log().Info(ctx, "修改密码成功", "user_id", userId)
	
	return nil
}

// UpdateProfile 更新个人信息
func (s *UserService) UpdateProfile(ctx context.Context, userId int, req *UpdateProfileReq) (*entity.User, error) {
	// 获取当前用户信息
	user, err := dao.User.GetById(ctx, userId)
	if err != nil {
		return nil, gerror.Wrap(err, "获取用户信息失败")
	}
	if user == nil {
		return nil, gerror.New("用户不存在")
	}
	
	// 验证邮箱格式
	if req.Email != "" && req.Email != user.Email {
		if err := validate.Email(req.Email); err != nil {
			return nil, err
		}
		
		// 检查邮箱是否已存在
		exists, err := dao.User.IsEmailExists(ctx, req.Email)
		if err != nil {
			return nil, gerror.Wrap(err, "检查邮箱失败")
		}
		if exists {
			return nil, gerror.New("邮箱已存在")
		}
	}
	
	// 验证手机号格式
	if req.Phone != "" {
		if err := validate.Phone(req.Phone); err != nil {
			return nil, err
		}
	}
	
	// 更新用户信息
	updateUser := &entity.User{
		Email:    req.Email,
		Phone:    req.Phone,
		Nickname: req.Nickname,
		Avatar:   req.Avatar,
	}
	
	err = dao.User.Update(ctx, userId, updateUser)
	if err != nil {
		return nil, gerror.Wrap(err, "更新用户信息失败")
	}
	
	// 获取更新后的用户信息
	user, err = dao.User.GetById(ctx, userId)
	if err != nil {
		return nil, gerror.Wrap(err, "获取更新后用户信息失败")
	}
	
	g.Log().Info(ctx, "更新个人信息成功", "user_id", userId)
	
	return user, nil
}

// GetProfile 获取个人信息
func (s *UserService) GetProfile(ctx context.Context, userId int) (*entity.User, error) {
	user, err := dao.User.GetById(ctx, userId)
	if err != nil {
		return nil, gerror.Wrap(err, "获取用户信息失败")
	}
	if user == nil {
		return nil, gerror.New("用户不存在")
	}
	
	return user, nil
}

// ValidateToken 验证令牌
func (s *UserService) ValidateToken(ctx context.Context, token string) (*entity.User, error) {
	// 验证令牌格式
	if err := jwt.ValidateTokenFormat(token); err != nil {
		return nil, err
	}
	
	// 获取令牌信息
	tokenInfo, err := dao.UserToken.GetByToken(ctx, token)
	if err != nil {
		return nil, gerror.Wrap(err, "获取令牌信息失败")
	}
	if tokenInfo == nil {
		return nil, gerror.New("令牌无效")
	}
	
	// 检查令牌类型
	if tokenInfo.TokenType != "access" {
		return nil, gerror.New("令牌类型错误")
	}
	
	// 检查令牌是否过期
	if tokenInfo.ExpiresAt.Before(gtime.Now()) {
		return nil, gerror.New("令牌已过期")
	}
	
	// 获取用户信息
	user, err := dao.User.GetById(ctx, tokenInfo.UserId)
	if err != nil {
		return nil, gerror.Wrap(err, "获取用户信息失败")
	}
	if user == nil {
		return nil, gerror.New("用户不存在")
	}
	
	// 检查用户状态
	if user.Status != "active" {
		return nil, gerror.New("用户账户已被锁定或停用")
	}
	
	return user, nil
}

// validateUsername 验证用户名格式
func (s *UserService) validateUsername(username string) error {
	username = strings.TrimSpace(username)
	
	if len(username) < 3 || len(username) > 20 {
		return gerror.New("用户名长度为3-20位")
	}
	
	// 检查用户名只包含字母、数字和下划线
	for _, char := range username {
		if !((char >= 'a' && char <= 'z') || (char >= 'A' && char <= 'Z') || 
			 (char >= '0' && char <= '9') || char == '_') {
			return gerror.New("用户名只能包含字母、数字和下划线")
		}
	}
	
	// 用户名不能以数字开头
	if username[0] >= '0' && username[0] <= '9' {
		return gerror.New("用户名不能以数字开头")
	}
	
	return nil
}

// getClientIP 获取客户端IP地址
func (s *UserService) getClientIP(r *ghttp.Request) string {
	if r == nil {
		return ""
	}
	
	// 尝试从各种头部获取真实IP
	ip := r.Header.Get("X-Forwarded-For")
	if ip != "" {
		// X-Forwarded-For可能包含多个IP，取第一个
		ips := strings.Split(ip, ",")
		if len(ips) > 0 {
			return strings.TrimSpace(ips[0])
		}
	}
	
	ip = r.Header.Get("X-Real-IP")
	if ip != "" {
		return ip
	}
	
	return r.GetClientIp()
}